package com.easy.cloud.web.service.upms.biz.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import com.easy.cloud.web.component.core.constants.GlobalCommonConstants;
import com.easy.cloud.web.component.core.exception.BusinessException;
import com.easy.cloud.web.component.core.tenant.TenantBroker;
import com.easy.cloud.web.component.core.util.BeanUtils;
import com.easy.cloud.web.service.upms.api.dto.MenuDTO;
import com.easy.cloud.web.service.upms.api.dto.MenuExcelDTO;
import com.easy.cloud.web.service.upms.api.vo.MenuVO;
import com.easy.cloud.web.service.upms.biz.converter.MenuConverter;
import com.easy.cloud.web.service.upms.biz.domain.MenuDO;
import com.easy.cloud.web.service.upms.biz.domain.RoleDO;
import com.easy.cloud.web.service.upms.biz.repository.MenuRepository;
import com.easy.cloud.web.service.upms.biz.repository.RoleRepository;
import com.easy.cloud.web.service.upms.biz.service.IMenuService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Menu 业务逻辑
 *
 * @author Fast Java
 * @date 2023-08-03 14:45:40
 */
@Slf4j
@Service
@AllArgsConstructor
public class MenuServiceImpl implements IMenuService {

    private final MenuRepository menuRepository;

    private final RoleRepository roleRepository;

    @Override
    @Transactional(rollbackOn = Exception.class)
    public void init() {
        // 未初始化过数据
        if (menuRepository.count() <= 0) {
            // 初始化菜单数据
            List<MenuExcelDTO> menuExcels = this.initJsonToList("json/sys_menu.json", MenuExcelDTO.class);
            // 构建存储数据
            List<MenuDO> menus = new ArrayList<>();
            this.recursionMenu(menuExcels, menus);
            menuRepository.saveAll(menus);
            menus.stream()
                    .filter(menu -> StrUtil.isNotBlank(menu.getName()))
                    .forEach(menu ->
                            menus.stream()
                                    .filter(menuDO -> StrUtil.isNotBlank(menuDO.getParentId()))
                                    .filter(menuDO -> menu.getName().equals(menuDO.getParentId()))
                                    .forEach(menuDO -> menuDO.setParentId(menu.getId()))
                    );
            log.info("init platform menus content success!");
        }
    }

    /**
     * 递归遍历所有菜单项
     *
     * @param menuExcels 导入的数据
     * @param menus      存储集合
     */
    private void recursionMenu(List<MenuExcelDTO> menuExcels, List<MenuDO> menus) {
        for (MenuExcelDTO menuExcel : menuExcels) {
            if (CollUtil.isNotEmpty(menuExcel.getChildren())) {
                this.recursionMenu(menuExcel.getChildren(), menus);
            }
            // 加入集合
            menus.add(menuExcel.convertTo(MenuDO.class));
        }
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    public MenuVO save(MenuDTO menuDTO) {
        // 转换成DO对象
        MenuDO menu = MenuConverter.convertTo(menuDTO);
        // TODO 校验逻辑

        // 父级名称默认
        if (StrUtil.isBlank(menuDTO.getParentId())) {
            menuDTO.setParentId(GlobalCommonConstants.DEPART_TREE_ROOT_ID);
        }

        // 同一层级下菜单相同校验
        Optional<MenuDO> deptOptional = menuRepository.findByNameAndParentId(menu.getName(), menu.getParentId());
        if (deptOptional.isPresent()) {
            throw new BusinessException("当前菜单信息已存在");
        }

        // 存储
        menuRepository.save(menu);
        // 转换对象
        return MenuConverter.convertTo(menu);
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    public MenuVO update(MenuDTO menuDTO) {
        // 更新操作，ID不能为空
        if (Objects.isNull(menuDTO.getId())) {
            throw new RuntimeException("当前更新对象ID为空");
        }

        // 若父级ID为空，则设置默认父级ID
        if (StrUtil.isBlank(menuDTO.getParentId())) {
            menuDTO.setParentId(GlobalCommonConstants.DEPART_TREE_ROOT_ID);
        }

        // 获取当前菜单信息
        MenuDO menuDO = menuRepository.findById(menuDTO.getId())
                .orElseThrow(() -> new BusinessException("当前菜单信息不存在"));
        // 将修改的数据赋值给数据库数据
        BeanUtils.copyProperties(menuDTO, menuDO, true);
        // 存储
        menuRepository.save(menuDO);
        // 转换对象
        return MenuConverter.convertTo(menuDO);
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    public Boolean removeById(String menuId) {
        // 判断当前菜单下是否存在子菜单
        if (menuRepository.countByParentId(menuId) > 0) {
            throw new BusinessException("当前菜单下存在子菜单，请先删除子菜单");
        }

        // 获取当前菜单信息，可以不删除关联表，因为前端展示的一般为主体信息，主体信息已删除，那么关联表意义就不大，后续关联表恢复数据也很重要
        menuRepository.logicDelete(menuId);
        return true;
    }

    @Override
    public MenuVO detailById(String menuId) {
        // TODO 业务逻辑校验

        // 删除
        MenuDO menu = menuRepository.findById(menuId)
                .orElseThrow(() -> new RuntimeException("当前数据不存在"));
        // 转换对象
        return MenuConverter.convertTo(menu);
    }

    @Override
    public List<Tree<String>> tree(String parentId, List<String> channels) {
        // 菜单集合
        List<MenuDO> menus = new ArrayList<>();
        // 如果是超级管理员，拥有所有权限
        if (channels.contains(GlobalCommonConstants.SUPER_ADMIN_ROLE)) {
            // 超管，无租户查询所有菜单
            menus = TenantBroker.applyAsNoTenant(tenantId -> menuRepository.findAll());
        } else if (channels.contains(GlobalCommonConstants.TENANT_ROLE)) {
            // 租户查询所有角色菜单
            Optional<RoleDO> roleOptional = TenantBroker.applyAsNoTenant(tenantId ->
                    roleRepository.findFirstByCode(GlobalCommonConstants.TENANT_ROLE));
            if (roleOptional.isPresent()) {
                menus = roleOptional.get().getMenus();
            }

        } else {
            // 查询当前同租户下的所有角色
            List<RoleDO> roles = roleRepository.findAllByCodeIn(channels);
            // 根据角色查询无租户状态下的所有菜单
            menus = TenantBroker.applyAsNoTenant(tenant -> roles.stream()
                    .flatMap(role -> role.getMenus().stream()).collect(Collectors.toList()));
        }
        return TreeUtil
                .build(menus, GlobalCommonConstants.DEPART_TREE_ROOT_ID, (menu, tree) -> {
                    tree.setId(menu.getId());
                    tree.setName(menu.getName());
                    tree.setParentId(menu.getParentId());
                    tree.setWeight(menu.getSort());
                    tree.putAll(BeanUtil.beanToMap(menu));
                });
    }
}